/**
 * Copyright (c) 2011 eelpo.com
 * All rights reserved.
 */
package com.eelpo.common.security;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;

import com.eelpo.common.encoder.BASE64DecoderStream;
import com.eelpo.common.encoder.BASE64EncoderStream;
import com.eelpo.common.security.constant.Constants;

/**
 * 功能描述：DES对称算法信息加密器
 * <p>
 * 版权所有：广州市艾尔博科技有限公司
 * <p>
 * 未经本公司许可，不得以任何方式复制或使用本程序任何部分
 * 
 * @author <a href="mailto:Brian@eelpo.com">汪洪州</a>
 * @since 1.0
 */
public class DESEncryptor {

	// public static final String DES_ALGORITHM = "DES";
	// public static final String DES_OFB32_PKCS5PADDING
	// ="DES/OFB32/PKCS5Padding";
	// public static final String DES_CFB8_NOPADDING = "DES/CFB8/NoPadding";

	/**
	 * <p>
	 * 方法用途和描述：加密字符串
	 * </p>
	 * 
	 * @param plainText
	 *            ：要加密的字符串
	 * @return 返回加密后的密文
	 */
	public static String encrypt(String plainText) {

		byte[][] IvWithPassword = getIvWithPassword();
		byte[] ivBytes = IvWithPassword[0];
		byte[] password = IvWithPassword[1];

		// 进行加密
		byte[] cipherBytes = getCipherBytes(ivBytes, password, plainText.getBytes());

		// 使用６４位字节加码器加码返回
		return BASE64EncoderStream.encode(ivBytes).concat(BASE64EncoderStream.encode(password)).concat(BASE64EncoderStream.encode(cipherBytes));

	}

	/**
	 * <p>
	 * 方法用途和描述：加密文节流
	 * </p>
	 * 
	 * @param plainBytes
	 *            ：需要加密的字节数组
	 * @return 返回加密后的字节数组
	 */
	public static byte[] encrypt(byte[] plainBytes) {

		byte[][] IvWithPassword = getIvWithPassword();
		byte[] ivBytes = IvWithPassword[0];
		byte[] password = IvWithPassword[1];

		// 进行加密
		byte[] cipherBytes = getCipherBytes(ivBytes, password, plainBytes);

		// 对（IV,加密密码,加密密文）进行组合
		byte[] ivWithPasswordAndCipher = new byte[cipherBytes.length + 16];
		System.arraycopy(ivBytes, 0, ivWithPasswordAndCipher, 0, 8);
		System.arraycopy(password, 0, ivWithPasswordAndCipher, 8, 8);
		System.arraycopy(cipherBytes, 0, ivWithPasswordAndCipher, 16, cipherBytes.length);

		// 返回（IV+加密密码+密文）
		return ivWithPasswordAndCipher;

	}

	/**
	 * <p>
	 * 方法用途和描述：加密文件
	 * </p>
	 * 
	 * @param inputPath
	 *            ：需要加密的文件路径
	 * @param outputPath
	 *            ：加密输出文件路径
	 */
	public static void encrypt(String inputPath, String outputPath) {

		byte[][] IvWithPassword = getIvWithPassword();
		byte[] ivBytes = IvWithPassword[0];
		byte[] password = IvWithPassword[1];

		try {

			InputStream inputStream = new FileInputStream(inputPath);
			OutputStream outputStream = new FileOutputStream(outputPath);

			Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, password, ivBytes);

			CipherOutputStream cOutputStream = new CipherOutputStream(outputStream, cipher);

			// 将IV和密码写入文件
			outputStream.write(ivBytes);
			outputStream.write(password);
			for (int i = inputStream.read(); i != -1; i = inputStream.read())
				cOutputStream.write(i);

			inputStream.close();
			cOutputStream.close();
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：解密字符串
	 * </p>
	 * 
	 * @param cipherText
	 *            ：IV+随机盐+密文的组合字符串
	 * @return 返回明文字符串
	 */
	public static String decrypt(String cipherText) {

		// 对字符串（IV+加密密码+密文）进行分解
		String ivStr = cipherText.substring(0, 12);
		String passwordStr = cipherText.substring(12, 24);
		String cipherStr = cipherText.substring(24);

		try {

			// 使用６４位字节解码器对字符串（IV+加密密码+密文）解码
			byte[] ivBytes = BASE64DecoderStream.decode(ivStr);
			byte[] password = BASE64DecoderStream.decode(passwordStr);
			byte[] cipherBytes = BASE64DecoderStream.decode(cipherStr);

			return new String(getPlainBytes(ivBytes, password, cipherBytes));

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/**
	 * <p>
	 * 方法用途和描述：解密字节数组
	 * </p>
	 * 
	 * @param cipherBytes
	 *            ：IV+随机盐+密文的组合字节数组
	 * @return 返回明文字节数组
	 */
	public static byte[] decrypt(byte[] cipherBytes) {

		byte[] ivBytes = new byte[DESKeySpec.DES_KEY_LEN];
		byte[] password = new byte[DESKeySpec.DES_KEY_LEN];
		byte[] pCipherBytes = new byte[cipherBytes.length - 16];

		// 对IV+加密密码+密文进行分解
		System.arraycopy(cipherBytes, 0, ivBytes, 0, 8);
		System.arraycopy(cipherBytes, 8, password, 0, 8);
		System.arraycopy(cipherBytes, 16, pCipherBytes, 0, pCipherBytes.length);

		return getPlainBytes(ivBytes, password, pCipherBytes);

	}

	/**
	 * <p>
	 * 方法用途和描述：解密文件
	 * </p>
	 * 
	 * @param inputPath
	 *            ：需要解密的文件路径
	 * @param outputPath
	 *            ：解密后输出文件路径
	 */
	public static void decrypt(String inputPath, String outputPath) {

		try {

			byte[] ivBytes = new byte[DESKeySpec.DES_KEY_LEN];
			byte[] password = new byte[DESKeySpec.DES_KEY_LEN];

			InputStream inputStream = new FileInputStream(inputPath);
			inputStream.read(ivBytes, 0, DESKeySpec.DES_KEY_LEN);
			inputStream.read(password, 0, DESKeySpec.DES_KEY_LEN);

			Cipher cipher = getCipher(Cipher.DECRYPT_MODE, password, ivBytes);

			CipherInputStream cInputStream = new CipherInputStream(inputStream, cipher);

			OutputStream outputStream = new FileOutputStream(outputPath);
			for (int i = cInputStream.read(); i != -1; i = cInputStream.read())
				outputStream.write(i);

			inputStream.close();
			cInputStream.close();
			outputStream.close();

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/*
	 * <p> 方法用途和描述：核心加密方法，进行纯加密 </p>
	 * 
	 * @param ivBytes：随机Initializatin Vector(IV)
	 * 
	 * @param password：随机加密密码
	 * 
	 * @param plainBytes：明文字节数组
	 * 
	 * @return 返回密文字节数组
	 */
	private static byte[] getCipherBytes(byte[] ivBytes, byte[] password, byte[] plainBytes) {

		try {

			Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, password, ivBytes);

			return cipher.doFinal(plainBytes);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/*
	 * <p> 方法用途和描述：核心加密方法，进行纯解密 </p>
	 * 
	 * @param ivBytes：随机Initializatin Vector(IV)
	 * 
	 * @param password：随机加密密码
	 * 
	 * @param cipherBytes：密文字节数组
	 * 
	 * @return 返回明文字节数组
	 */
	private static byte[] getPlainBytes(byte[] ivBytes, byte[] password, byte[] cipherBytes) {

		try {

			Cipher cipher = getCipher(Cipher.DECRYPT_MODE, password, ivBytes);

			return cipher.doFinal(cipherBytes);

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/*
	 * <p> 方法用途和描述：获取加密解密器 </p>
	 * 
	 * @param mode：算法模式
	 * 
	 * @param password：随机加密密码
	 * 
	 * @param ivBytes：随机Initializatin Vector(IV)
	 * 
	 * @return 返回加密解密器对象
	 */
	private static Cipher getCipher(int mode, byte[] password, byte[] ivBytes) {

		try {

			// 生成密钥内容的规范DESKeySpec
			DESKeySpec keySpec = new DESKeySpec(password);
			Key key = KeyBuilder.getInstance().getSymmetryKey(Constants.DES_ALGORITHM, keySpec);

			// 创建Initializatin Vector(IV)参数规范
			IvParameterSpec iv = new IvParameterSpec(ivBytes);

			Cipher cipher = Cipher.getInstance(Constants.DES_CFB8_NOPADDING);
			cipher.init(mode, key, iv);

			return cipher;

		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

	}

	/*
	 * <p> 方法用途和描述：获取随机Initializatin Vector(IV)和随机密码 </p>
	 * 
	 * @return 返回随机Initializatin Vector(IV)和随机密码
	 */
	private static byte[][] getIvWithPassword() {

		byte[][] ivWithPassword = new byte[2][DESKeySpec.DES_KEY_LEN];

		SecureRandom random = new SecureRandom();

		// 随机创建Initializatin Vector(IV),IV的长度必须等于DESKeySpec.DES_KEY_LEN
		random.nextBytes(ivWithPassword[0]);

		// 随机创建一个密码,password的长度必须等于DESKeySpec.DES_KEY_LEN
		random.nextBytes(ivWithPassword[1]);

		return ivWithPassword;

	}
}
